use std::borrow::Cow;
use std::fmt::{self, Display, Formatter};

/// 在终端显示带颜色的字符，需要使用 ANSI 转义序列 (ANSI Escape Code)。
/// ANSI 转义 序列就是指形如 ESC 和 [组合而成的字符序列，可以实现在屏幕上定位光标或改变输出字符
/// 颜色等功能，所以也被称为控制字符，被定义于 ASCII码中。 ESC有三种表示方法:
/// · 在 Shell 中表示为\e。
/// · 以 ASCII 十六进制表示为\x1B。
/// . 以 ASCII 八进制表示为\033。
/// 所以 ，如果想在终端输出带指定颜色的字符 Hello，需要将其变为包含 ANSI 转义序列的 字符串，如下所示
/// echo "\e[31;43mHello\e[Om"
/// echo "\x1B[31;43mHello\x1B[Om"
/// echo "\033[31;43mHello\033[Om"
/// \x1B[为前缀，表示这是一个 ANSI控制序列的开始。用分号相隔的31;43属于颜色代码，
/// 31 是前景 ，代表红色; 43为背景色，代表黄色。字母m为结束符，原始文本 Hello 置于其后。
/// 最后 的\x1B[Om 结尾代表重置全部属性，表示一个 ANSI 控制序列的结束。
pub fn start() {
    let colored_string = ColoredString {
        content: "这是测试".to_string(),
        fg_color: Some(Color::RED),
        bg_color: Some(Color::YELLOW),
    };
    println!("{}", colored_string);
}

struct ColoredString {
    content: String,
    fg_color: Option<Color>,
    bg_color: Option<Color>,
}

impl ColoredString {
    fn format(&self) -> Cow<String> {
        if self.content.is_empty() {
            return Cow::Borrowed(&self.content);
        }
        let ColoredString {
            content,
            fg_color,
            bg_color,
        } = self;
        match (content, fg_color, bg_color) {
            (content, Some(fg), Some(bg)) => Cow::Owned(format!(
                "\x1B[{};{}m{}\x1B[Om",
                fg.display(),
                bg.display(),
                content
            )),
            (content, None, Some(bg)) => {
                Cow::Owned(format!("\x1B[;{}m{}\x1B[Om", bg.display(), content))
            }
            (content, Some(fg), None) => {
                Cow::Owned(format!("\x1B[{}m{}\x1B[Om", fg.display(), content))
            }
            _ => Cow::Borrowed(&self.content),
        }
    }
}

impl Display for ColoredString {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.format())
    }
}

enum Color {
    RED,
    YELLOW,
}

impl Color {
    fn display(&self) -> &str {
        match self {
            Color::RED => "31",
            Color::YELLOW => "43",
        }
    }
}
